package com.zelix.analy;

import com.arthas.client.ArthasClient;
import com.arthas.client.ArthasTool;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;

import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class singleCmd {
    public static void main(String[] args) {
        ArthasClient client = ArthasTool.getClient();

        if (client == null) {
            return;
        }

        fieldAndMethod(client, "m", "8342609217386135549", "46330256718121");
        //decryStr(client, "com.zelix.ps", "b", 17599, "247020047979160497", "106409953578473");

        client.disconnect();
    }

    public static void decryStr(ArthasClient client, String cls, String methodName, int intValue, String longValue, String key) {

        String classLoader = ArthasTool.getClassLoader(client, cls);
        if (classLoader == null) {
            System.out.println("类 " + cls + " 还未加载,请点击相应功能触发其加载.");
            return;
        }

        String base64StringMethodFormat = "ognl '#str=@" + cls.replaceAll("/", ".") + "@" + methodName + "(%d,%sL^%sL),@java.util.Base64@getEncoder().encodeToString(#str.getBytes(\"utf-8\"))' -c " + classLoader;

        String rtn = client.sendCommand(String.format(base64StringMethodFormat, intValue, longValue, key));
        System.out.println("字符串解密:" + rtn);
        System.out.println("\"" + new String(Base64.getDecoder().decode(ArthasTool.removeStringLebal(rtn)), StandardCharsets.UTF_8) + "\"");
    }

    public static AbstractInsnNode getFieldAndMethodNode(ArthasClient client, String name, String longValue, String key) {
        String cls = "com.zelix.v99";
        String fieldName = "c";
        String methodName = "d";

        String classLoader = ArthasTool.getClassLoader(client, cls);
        if (classLoader == null) {
            System.out.println("类 " + cls + " 还未加载,请点击相应功能触发其加载.");
            return null;
        }


        //获取asm描述Method的ognl表示方式
        String asmMethodFormat = "ognl '#method=@%s@%s(%sL,%sL),#des=@jdk.internal.org.objectweb.asm.Type@getType(#method),#ower=@jdk.internal.org.objectweb.asm.Type@getType(#method.getDeclaringClass()),#ower+\":\"+#method.getName()+\":\"+#des' -c " + classLoader;
        //获取asm描述Field的ognl表示方式
        String asmFieldFormat = "ognl '#field=@%s@%s(%sL,%sL),#des=@jdk.internal.org.objectweb.asm.Type@getType(#field.getType()),#ower=@jdk.internal.org.objectweb.asm.Type@getType(#field.getDeclaringClass()),#ower+\":\"+#field.getName()+\":\"+#des' -c " + classLoader;

        String rtn = "";
        int opcode = -1;
        int tag = getTagInt(name, Long.valueOf(key));
        if (tag == 110 || tag == 112 || tag == 115 || tag == 109) {
            String cmd = String.format(asmFieldFormat, cls, fieldName, longValue, key);
            rtn = client.sendCommand(cmd);
            if (!rtn.startsWith("@String[")) {
                System.err.println("属性加密执行错误:" + cmd);
                System.out.println("属性加密执行错误:" + rtn);
                return null;
            }
            rtn = asmdes(rtn);
            if (rtn == null) {
                return null;
            }
            String[] des = rtn.split(":");
            if (tag == 110) {
                opcode = Opcodes.GETFIELD;
            } else if (tag == 112) {
                opcode = Opcodes.PUTFIELD;
            } else if (tag == 115) {
                opcode = Opcodes.GETSTATIC;
            } else {
                opcode = Opcodes.PUTSTATIC;
            }
            return new FieldInsnNode(opcode, des[0], des[1], des[2]);
        } else {
            String cmd = String.format(asmMethodFormat, cls, methodName, longValue, key);
            rtn = client.sendCommand(cmd);
            if (!rtn.startsWith("@String[")) {
                System.err.println("方法加密执行错误:" + cmd);
                System.out.println("方法加密执行错误:" + rtn);
                return null;
            }
            rtn = asmdes(rtn);
            String[] des = rtn.split(":");
            if (tag == 111) {
                opcode = Opcodes.INVOKEVIRTUAL;
            } else if (tag == 108) {
                opcode = Opcodes.INVOKESTATIC;
            } else {
                opcode = Opcodes.INVOKESPECIAL;
            }
            return new MethodInsnNode(opcode, des[0], des[1], des[2]);
        }
    }

    public static void fieldAndMethod(ArthasClient client, String name, String longValue, String key) {

        String cls = "com.zelix.v99";
        String fieldName = "c";
        String methodName = "d";

        String classLoader = ArthasTool.getClassLoader(client, cls);
        if (classLoader == null) {
            System.out.println("类 " + cls + " 还未加载,请点击相应功能触发其加载.");
            return;
        }

        String reflectMethodFormat = "ognl '@%s@%s(%sL,%sL).toString' -c " + classLoader;

        String cmd = "";
        String opcode = "";
        int tag = getTagInt(name, Long.valueOf(key));
        if (tag == 110 || tag == 112 || tag == 115 || tag == 109) {
            cmd = String.format(reflectMethodFormat, cls, fieldName, longValue, key);
            if (tag == 110) {
                opcode = "GETFIELD";
            } else if (tag == 112) {
                opcode = "PUTFIELD";
            } else if (tag == 115) {
                opcode = "GETSTATIC";
            } else {
                opcode = "PUTSTATIC";
            }
        } else {
            cmd = String.format(reflectMethodFormat, cls, methodName, longValue, key);
            if (tag == 111) {
                opcode = "INVOKEVIRTUAL";
            } else if (tag == 108) {
                opcode = "INVOKESTATIC";
            } else {
                opcode = "INVOKESPECIAL";
            }
        }

        //方法
        String rtn = client.sendCommand(cmd);
        System.out.println(opcode + ":" + ArthasTool.removeStringLebal(rtn));
    }

    public static int getTagInt(String tag, long l) {
        int i = (int) l;
        i = i & 7;
        i = tag.charAt(0) ^ i;
        return i;
    }

    /**
     * 返回ower+name+des的形式
     *
     * @param rtn
     * @return
     */
    public static String asmdes(String rtn) {
        rtn = ArthasTool.removeStringLebal(rtn);
        String[] split = rtn.split(":");
        if (split.length == 3) {
            if (split[0].startsWith("L") && split[0].endsWith(";")) {//owner应该是对象格式
                return split[0].substring(1, split[0].length() - 1) + ":" + split[1] + ":" + split[2];
            }
        }
        System.err.println("命令执行错误:" + rtn);
        return null;
    }

}
